#include <unistd.h>
#include <iostream>
using namespace std;
#include <string>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <ctype.h>


#define OutPutredir 1
#define InPutredir 2
#define Appredir 3

#define RemovePlace(pos) do { while(isspace((*pos))) pos++ ; }while(0)




char* filename = nullptr;
int retdir = 0;



const int basesize = 1024;
const int gnum = 64;

int gargc = 0;
char* gargv[gnum];
char* genv[basesize];
char* retenv = nullptr;

char pwdenv[basesize];
char cwdarr[basesize];

int lastnode = 0;

string GetUName()
{
    char* name = getenv("USER");
    if (name == nullptr) return "NONE";
    return name;
}

//修改pwd
void UpdateEnv(char* chpwd)
{
    for (int i = 0; genv[i]; i++)
    {
        if (strncmp("PWD",genv[i],3) == 0)
        {
            snprintf(genv[i],basesize,"%s",chpwd);
            return;
        }
    }
}


string GetPwd()
{
    if (!getcwd(cwdarr,basesize)) return "NONE";
     
    snprintf(pwdenv,basesize,"PWD=%s",cwdarr);
    UpdateEnv(pwdenv);
    
    return cwdarr;
}
string GetHostname()
{
    char* hostname = getenv("HOSTNAME");
    if (hostname == nullptr) return "NONE";
    return hostname;
}


bool GetCmdLine(char* cmdline)
{
   char* ret  = fgets(cmdline,basesize,stdin);
   if (ret == nullptr || strlen(cmdline) == 1) return false;
   cmdline[strlen(cmdline)-1] = '\0'; //消除最后一个\n
   return true;
}

string GetNewPwd()
{
    string _path = GetPwd();
    if (_path == "/") return "/";
    return _path.substr(_path.rfind("/")+1);
}

void PrinfCmdLine()
{
    printf("[%s@%s %s]# ",GetUName().c_str(),GetHostname().c_str(),GetNewPwd().c_str());
    fflush(stdout);
}

bool ParseCmdLine(char* cmdline)
{
    gargc = 0;
    memset(gargv,0,sizeof(gargv)); //重置命令行参数表
    const char* sep = " ";         //strtok以空格为分割符
    filename = nullptr; 
    retdir = 0;

    int pos = strlen(cmdline)-1;
    while(pos >= 0)
    {
        if (cmdline[pos] == '>') 
        {
            if (pos > 0 && cmdline[pos-1] == '>') //重定向追加
            {
                cmdline[pos-1] = 0;
                retdir = Appredir;
                filename = &(cmdline[pos+1]);
                RemovePlace(filename);
                break;
            }
            else  // > 
            {
               cmdline[pos] = 0; 
               retdir = OutPutredir;
               filename = &(cmdline[pos+1]);
               RemovePlace(filename);
               break;
            }
        }
        else if(cmdline[pos] == '<')
        {

              cmdline[pos] = 0; 
              retdir = InPutredir;
              filename = &(cmdline[pos+1]);
              RemovePlace(filename);
              break;
        }
        else pos--;
    }
    
    char* tmp = strtok(cmdline,sep);
    if (tmp == nullptr && retdir)
    {
        gargv[gargc] = nullptr;   //命令行参数表最后需要添加一个空指针
        return true;
    }
    if (tmp == nullptr) return false;
    gargv[gargc] = (char*)malloc(strlen(tmp) + 1);
    strcpy(gargv[gargc],tmp);
    gargc++;
    while((bool)(tmp = strtok(nullptr,sep)))
    {
        gargv[gargc] = (char*)malloc(strlen(tmp)+1);
        strcpy(gargv[gargc],tmp);
        gargc++;
    }    
    gargv[gargc] = nullptr;   //命令行参数表最后需要添加一个空指针
    return true;
}

void ExcuteCmdline()
{
    pid_t id = fork();
    if (id == 0)
    {
        
        if (retdir == 1) 
        {
            int fd = open(filename,O_WRONLY | O_TRUNC | O_CREAT, 0666);
            dup2(fd,1);
            if (gargc == 0)
            {
                printf("");
                fflush(stdout);
            }
        }
        else if(retdir == 2)
        {
            int fd = open(filename,O_RDONLY);
            dup2(fd,0);

        }
        else if(retdir == 3)
        {
            int fd = open(filename,O_WRONLY | O_APPEND | O_CREAT, 0666);
            dup2(fd,1);
            if (gargc == 0)
            {
                printf("");
                fflush(stdout);
            }

        }
        if (execvpe(gargv[0],gargv,genv) < 0)
        {
            printf("commend not found\n");
            exit(1);
        }
        exit(0);
    }
    int status;
    pid_t w = waitpid(id,&status,0);
    if(w < 0)
    {
        printf("等待失败!\n");
    }
    else
    {
        if (WIFEXITED(status))
        {
            lastnode = WEXITSTATUS(status);
        }
        else
        {
            lastnode = 1; //代表异常结束
        }
    }
}

void AddEnv(char* newenv)
{
    int index = 0;
    while(genv[index])
    {
        ++index;
    }
    genv[index] = (char*)malloc(strlen(newenv)+1);
    strcpy(genv[index],newenv);
}


void PrintEnv()
{
    int index = 0;
    while(genv[index])
    {
        printf("%s\n", genv[index]);
        index++;
    }
}

char* GetMyEnv(char* name)
{
    int i = 0, len = strlen(name);
    while(genv[i])
    {
        if (strncmp(genv[i],name,len) == 0)
        {
            retenv = genv[i] + len+1;
            return retenv;
        }
        i++;
    }
    return nullptr;

}

bool CheckAndExcuteCmdline()
{
    int ret= 0 ;
    if (strcmp("cd",gargv[0]) == 0)
    {
        if (gargc == 2)
        {

            if ((bool)(ret = (chdir(gargv[1]) == -1))) cout << "No Such file or directory!" << endl;
            lastnode = ret;
            return true;
        }
    }
    else if(strcmp("export",gargv[0]) == 0)
    {
        if (gargc == 2)
        {
            AddEnv(gargv[1]);
            lastnode = 0;
            return true;
        }
    }
    else if(strcmp("env",gargv[0]) == 0)
    {
        if (gargc == 1)
        {
            PrintEnv();
            lastnode = 0;
            return true;
        }
    }
    else if(strcmp("echo", gargv[0]) == 0 && retdir == 0)
    {
        if (gargv[1][0] == '$')
        {
            if (gargv[1][1] == '?')
            {
                printf("%d\n", lastnode);
                lastnode = 0;
            }
            else  //echo $PATH
            {
                if (GetMyEnv(gargv[1]+1))
                {
                    printf("%s\n",retenv);
                    lastnode = 0;
                }   
                else lastnode = 1;
            }
        }
        else
        {
            printf("%s\n", gargv[1]);
        }
        return true;
    }
    return false;
}

void InitialEnv()
{
    extern char** environ;
    int index  = 0;
    while(environ[index])
    {
        genv[index] = (char*)malloc(strlen(environ[index])+1);
        strcpy(genv[index],environ[index]);
        index++;
    }
    genv[index] = nullptr;
}




int main()    
{    
    InitialEnv();    
    char cmdline[basesize];    
   //[xhm@iZ2zee7b26b1g3ujcquk70Z newshell]$     
   while(true)    
   {                                             
        //1.打印命令行                                
        PrinfCmdLine();    
        //2.命令行输入    
        if(!GetCmdLine(cmdline))    
        {               
            continue;               
        }                                  
        //3.解析命令行   
        //"ls -a -l -n" > file.txt
        //"ls -a -l -n" >> file.txt
        //"ls -a -l -n" < file.txt
        //需要分割字符串
        if (!ParseCmdLine(cmdline)) continue;    
        if (retdir == 0 &&  CheckAndExcuteCmdline()) continue; 
        //4.运行程序                 
        
        ExcuteCmdline();    
   }    
    return 0;    
}    

